﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;

namespace SystemHelperLibrary.API.Win7
{
	public class Win7Helper
	{
		private Win7Helper()
		{ }

		public const long DESKTOP_CREATEWINDOW = 0x0002L;
		public const long DESKTOP_ENUMERATE = 0x0040L;
		public const long DESKTOP_WRITEOBJECTS = 0x0080L;
		public const long DESKTOP_SWITCHDESKTOP = 0x0100L;
		public const long DESKTOP_CREATEMENU = 0x0004L;
		public const long DESKTOP_HOOKCONTROL = 0x0008L;
		public const long DESKTOP_READOBJECTS = 0x0001L;
		public const long DESKTOP_JOURNALRECORD = 0x0010L;
		public const long DESKTOP_JOURNALPLAYBACK = 0x0020L;

		public const uint MB_SERVICE_NOTIFICATION = 0x200000;

		public const long AccessRights =
			DESKTOP_JOURNALRECORD |
			DESKTOP_JOURNALPLAYBACK |
			DESKTOP_CREATEWINDOW |
			DESKTOP_ENUMERATE |
			DESKTOP_WRITEOBJECTS |
			DESKTOP_SWITCHDESKTOP |
			DESKTOP_CREATEMENU |
			DESKTOP_HOOKCONTROL |
			DESKTOP_READOBJECTS;

		public const long AccessRights2 = DESKTOP_CREATEWINDOW | DESKTOP_SWITCHDESKTOP;

		[StructLayout(LayoutKind.Sequential)]
		internal struct DWM_BLURBEHIND
		{
			public DWM_BB dwFlags;
			public bool fEnable;
			public IntPtr hRgnBlur;
			public bool fTransitionOnMaximized;
		}

		[Flags]
		internal enum DWM_BB
		{
			Enable = 1,
			BlurRegion = 2,
			TransitionMaximized = 4
		}

		[StructLayout(LayoutKind.Sequential)]
		internal struct DWM_THUMBNAIL_PROPERTIES
		{
			public uint dwFlags;
			public RECT rcDestination;
			public RECT rcSource;
			public byte opacity;
			public bool fVisible;
			public bool fSourceClientAreaOnly;
		}

		[StructLayout(LayoutKind.Sequential)]
		internal struct RECT
		{
			public int left;
			public int top;
			public int right;
			public int bottom;
		}

		[StructLayout(LayoutKind.Sequential)]
		public struct MARGINS
		{
			public int leftWidth;
			public int rightWidth;
			public int topHeight;
			public int bottomHeight;
		}

		[DllImport("shell32.dll")]
		internal static extern void GetCurrentProcessExplicitAppUserModelID([Out(), MarshalAs(UnmanagedType.LPWStr)] out StringBuilder AppID);

		[DllImport("user32.dll")]
		public static extern IntPtr CreateDesktop(String lpszDesktop, IntPtr lpszDevice, IntPtr pDevmode, int dwFlags,
													long dwDesiredAccess, IntPtr lpsa);

		[DllImport("user32.dll")]
		public static extern bool SetThreadDesktop(IntPtr hDesktop);

		[DllImport("user32.dll")]
		public static extern IntPtr GetThreadDesktop(int dwThreadId);

		[DllImport("kernel32.dll")]
		public static extern int GetCurrentThreadId();

		[DllImport("user32.dll")]
		public static extern bool SwitchDesktop(IntPtr hDesktop);

		[DllImport("dwmapi.dll")]
		internal static extern void DwmEnableBlurBehindWindow(IntPtr hwnd, ref DWM_BLURBEHIND blurBehind);

		[DllImport("dwmapi.dll", PreserveSig = false)]
		internal static extern void DwmGetColorizationColor(out uint ColorizationColor, [MarshalAs(UnmanagedType.Bool)]out bool ColorizationOpaqueBlend);

		[DllImport("dwmapi.dll")]
		internal static extern int DwmIsCompositionEnabled(out bool enabled);

		[DllImport("dwmapi.dll", PreserveSig = false)]
		internal static extern bool DwmIsCompositionEnabled();

		[DllImport("dwmapi.dll")]
		internal static extern int DwmpSetColorization(UInt32 ColorizationColor, bool ColorizationOpaqueBlend, UInt32 Opacity);

		[DllImport("dwmapi.dll", PreserveSig = false)]
		internal static extern void DwmQueryThumbnailSourceSize(IntPtr hThumbnail, out System.Drawing.Size size);

		[DllImport("dwmapi.dll", SetLastError = true)]
		internal static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);

		[DllImport("dwmapi.dll")]
		internal static extern int DwmUnregisterThumbnail(IntPtr thumb);

		[DllImport("dwmapi.dll", PreserveSig = false)]
		internal static extern int DwmUpdateThumbnailProperties(IntPtr hThumbnail, ref DWM_THUMBNAIL_PROPERTIES props);

		[DllImport("dwmapi.dll", PreserveSig = true)]
		internal static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

		public static string GetCurrentProcessExplicitAppUserModelIDApi()
		{
			StringBuilder data;
			GetCurrentProcessExplicitAppUserModelID(out data);
			return (data == null ? string.Empty : data.ToString());
		}

		public static void SwitchToFadeScreen()
		{
			//Get the current desktop
			IntPtr _hOrigDesktop = GetThreadDesktop(GetCurrentThreadId());
			//create our desktop
			IntPtr _hFadeDesktop = CreateDesktop("MyDesktop", IntPtr.Zero, IntPtr.Zero, 0, AccessRights, IntPtr.Zero);
			// set the thread desktop
			SetThreadDesktop(_hFadeDesktop);
			SwitchDesktop(_hFadeDesktop);
			SwitchDesktop(_hOrigDesktop);
			SetThreadDesktop(_hOrigDesktop);
		}

		public static bool IsAeroEnabled()
		{
			if (Environment.OSVersion.Version.Major < 6)
				return false;
			bool aeroEnbl;
			DwmIsCompositionEnabled(out aeroEnbl);
			return aeroEnbl;
		}

		public static MARGINS GetDpiAdjustedMargins(IntPtr windowHandle, int left, int right, int top, int bottom)
		{
			// Get System Dpi
			System.Drawing.Graphics desktop = System.Drawing.Graphics.FromHwnd(windowHandle);
			float DesktopDpiX = desktop.DpiX;
			float DesktopDpiY = desktop.DpiY;

			// Set Margins
			MARGINS margins = new MARGINS();

			// Note that the default desktop Dpi is 96dpi. The  margins are
			// adjusted for the system Dpi.
			margins.leftWidth = Convert.ToInt32(left * (DesktopDpiX / 96));
			margins.rightWidth = Convert.ToInt32(right * (DesktopDpiX / 96));
			margins.topHeight = Convert.ToInt32(top * (DesktopDpiX / 96));
			margins.bottomHeight = Convert.ToInt32(bottom * (DesktopDpiX / 96));

			return margins;
		}

		public static void ExtendGlass(IntPtr windowHandle)
		{
			ExtendGlass(windowHandle, -1, -1, -1, -1);
		}

		public static void ExtendGlass(IntPtr windowHandle, int left, int right, int top, int bottom)
		{
			internalExtendGlass(windowHandle, left, right, top, bottom);
		}

		private static int internalExtendGlass(IntPtr windowHandle, int left, int right, int top, int bottom)
		{
			var retVal = -1; // Returning less than zero will indicate that Aero Glass could not be extended
			// Calculate the Aero Glass Margins
			MARGINS margins = GetDpiAdjustedMargins(windowHandle, left, right, top, bottom);

			try
			{
				// Actually Enable Aero Glass
				retVal = DwmExtendFrameIntoClientArea(windowHandle, ref margins);
			}
			catch (Exception)
			{
				retVal = -1;
			}

			return retVal;
		}

		#region "WPF Static Methods"

		public static void ExtendGlass(System.Windows.Window win)
		{
			ExtendGlass(win, -1, -1, -1, -1);
		}

		public static void ExtendGlass(Window win, int left, int right, int top, int bottom)
		{
			System.Windows.Media.Brush originalBackgroundBrush = win.Background;
			try
			{
				int retVal = -1;
				if (DwmIsCompositionEnabled())
				{
					win.Background = Brushes.Transparent;

					System.Windows.Interop.WindowInteropHelper windowInterop = new System.Windows.Interop.WindowInteropHelper(win);
					IntPtr windowHandle = windowInterop.Handle;

					System.Windows.Interop.HwndSource mainWindowSrc = System.Windows.Interop.HwndSource.FromHwnd(windowHandle);
					mainWindowSrc.CompositionTarget.BackgroundColor = System.Windows.Media.Colors.Transparent;

					retVal = internalExtendGlass(windowHandle, left, right, top, bottom);
				}
				if (retVal < 0)
				{
					throw new NotSupportedException("Operation Failed: Aero Glass Not Supported");
				}
			}
			catch
			{
				// If an error occurred then change the Window.Background back to what it was
				win.Background = originalBackgroundBrush;
			}
		}

		#endregion

	}
}
